package com.atguigu.tingshu.account.service.impl;

import com.atguigu.tingshu.account.delay.DelayClient;
import com.atguigu.tingshu.account.delay.DelayUtil;
import com.atguigu.tingshu.account.mapper.UserAccountDetailMapper;
import com.atguigu.tingshu.account.mapper.UserAccountMapper;
import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.account.UserAccount;
import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.beans.Beans;
import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {

    @Autowired
    private UserAccountMapper userAccountMapper;

    @Autowired
    private RedisTemplate redisTemplates;

    @Autowired
    private UserAccountDetailMapper detailMapper;

    @Autowired
    private DelayClient delayClient;

    @Autowired
    private KafkaService kafkaService;

    @Transactional
    @Override
    public void saveUserAccount(Long userId) {
        UserAccount userAccount = new UserAccount();
        userAccount.setUserId(userId);
        this.save(userAccount);
    }

    @Transactional
    @Override
    public AccountLockResultVo checkLock(AccountLockVo accountLockVo) {
        AccountLockResultVo resultVo = null;
        //获取订单编号
        String orderNo = accountLockVo.getOrderNo();
        //保证锁定余额的幂等性
        Boolean absent = this.redisTemplates.opsForValue().setIfAbsent("account:lock:idem:" + orderNo, "", RedisConstant.CACHE_TIMEOUT, TimeUnit.SECONDS);
        if (!absent) {
            //查询缓存判断是否存在锁定信息 存在直接返回
            resultVo = (AccountLockResultVo) this.redisTemplates.opsForValue().get("account:lock:prefix:" + orderNo);
            if (resultVo != null) {
                return resultVo;
            } else {
                throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_ERROR);
            }
        }
        try {
            //校验余额     不存在校验可用金额>=订单价格
            UserAccount userAccount = this.userAccountMapper.check(accountLockVo.getUserId(), accountLockVo.getAmount());
            if (userAccount == null) {
                return null;
            }
            //锁余额
            //修改userAccount表
            if (this.userAccountMapper.lock(userAccount.getUserId(), accountLockVo.getAmount()) == 1) {
                //添加账户明细详情表
                UserAccountDetail accountDetail = new UserAccountDetail();
                BeanUtils.copyProperties(accountLockVo, accountDetail);
                accountDetail.setTradeType(SystemConstant.ACCOUNT_TRADE_TYPE_LOCK);
                accountDetail.setTitle("账户锁定：" + accountLockVo.getContent());
                this.detailMapper.insert(accountDetail);
                //返回并保存到Redis
                resultVo = new AccountLockResultVo();
                BeanUtils.copyProperties(accountLockVo, resultVo);
                this.redisTemplates.opsForValue().set("account:lock:prefix:" + orderNo, resultVo, RedisConstant.CACHE_TIMEOUT, TimeUnit.SECONDS);
            }
            //发送延时消息定时解锁余额 (无法判断未来是否会创建订单（也无法预知未来会不会扣减余额和解锁余额）)
            this.delayClient.setAttribute().sendUnlock(orderNo, 24, TimeUnit.HOURS);
            //返回锁定结果
            return resultVo;
        } catch (Exception e) {
            this.redisTemplates.delete("account:lock:idem:" + orderNo);
            throw new RuntimeException(e);
        }
    }

    @Transactional
    @Override
    public void reduceAccount(String orderNo) {
        //扣减余额幂等性判断
        Boolean absent = this.redisTemplates.opsForValue().setIfAbsent("account:reduce:idem" + orderNo, "", RedisConstant.CACHE_TIMEOUT, TimeUnit.SECONDS);
        if (!absent) {
            return;
        }
        try {
            //查询缓存锁定信息
            AccountLockResultVo resultVo = (AccountLockResultVo) this.redisTemplates.opsForValue().get("account:lock:prefix:" + orderNo);
            //如果缓存为空 直接返回
            if (resultVo == null) {
                return;
            }
            //缓存不为空  减余额 更新userAccount
            if (this.userAccountMapper.reduce(resultVo.getUserId(), resultVo.getAmount()) == 1) {
                //添加明细变动表数据
                UserAccountDetail accountDetail = new UserAccountDetail();
                BeanUtils.copyProperties(resultVo, accountDetail);
                accountDetail.setTradeType(SystemConstant.ACCOUNT_TRADE_TYPE_MINUS);
                accountDetail.setTitle("账户扣减：" + resultVo.getContent());
                accountDetail.setOrderNo(orderNo);
                this.detailMapper.insert(accountDetail);
                //删除锁定信息缓存
                this.redisTemplates.delete("account:lock:prefix:" + orderNo);
                //更新订单支付状态 远程调用
                this.kafkaService.sendMsg(KafkaConstant.QUEUE_ORDER_PAY_SUCCESS, orderNo);
            }
        } catch (Exception e) {
            this.redisTemplates.delete("account:reduce:idem" + orderNo);
            throw new RuntimeException(e);
        }
    }

    //解锁余额
    @Transactional
    @Override
    public void unlock(String orderNo) {
        //防止重复解锁
        Boolean absent = this.redisTemplates.opsForValue().setIfAbsent("account:unlock:idem" + orderNo, "", RedisConstant.CACHE_TIMEOUT, TimeUnit.SECONDS);
        if (!absent) {
            return;
        }
        try {
            AccountLockResultVo resultVo = (AccountLockResultVo) this.redisTemplates.opsForValue().get("account:lock:prefix:" + orderNo);
            if (resultVo == null) {
                return;
            }
            //解锁
            //修改user_account表
            if (this.userAccountMapper.unlock(resultVo.getUserId(), resultVo.getAmount()) == 1) {
                //新增user_account_detail表
                UserAccountDetail accountDetail = new UserAccountDetail();
                BeanUtils.copyProperties(resultVo, accountDetail);
                accountDetail.setTradeType(SystemConstant.ACCOUNT_TRADE_TYPE_UNLOCK);
                accountDetail.setTitle("账户解锁" + resultVo.getContent());
                accountDetail.setOrderNo(orderNo);
                this.detailMapper.insert(accountDetail);
                //删除缓存
                this.redisTemplates.delete("account:lock:prefix:" + orderNo);
            }
        } catch (BeansException e) {
            //出现异常 删除幂等性Redis的缓存
            this.redisTemplates.delete("account:unlock:idem");
            throw new RuntimeException(e);
        }
    }

    @Override
    public Page<UserAccountDetail> findUserMoneyRecord(Integer page, Integer limit, String tradeType) {
        return this.detailMapper.selectPage(new Page<UserAccountDetail>(page, limit), new LambdaQueryWrapper<UserAccountDetail>()
                .eq(UserAccountDetail::getUserId, AuthContextHolder.getUserId())
                .eq(UserAccountDetail::getTradeType, tradeType));
    }
}
